本文主要是对内核模式下的多线程机制进行讲述。与用户模式下的同步机制相比,使用内核对象的同步机制用途更加广泛。
内核对象包括以下几类
- 进程
- 线程
- 作业
- 事件
- 可等待的计时器
- 信号量
- 互斥量
等待函数
在使用内核对象的同步机制时,就要用到等待函数来判断内核对象是否已经触发,这样就可以确定调用线程是否可被调度。
WaitForSingleObject
1 | DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds); |
- hObject:表示要等待的内核对象
- dwMilliseconds:表示线程愿意花费多少时间来等待对象被触发(时间单位:毫秒),INFINITE表示无限等待
函数的返回值:如果线程等待的对象被触发了,则返回WAIT_object_0; 如果因为等待超时,返回WAIT_TIMEOUT; 如果给函数传入的是无限的句柄,则返回WAIT_FAILED;
WaitForMultipleObject
与WaitForSingleObject不同之处,WaitForMultipleObject可以等待多个内核对象。
1
2
3
4
5DWORD WaitForMultipleObject(
DWORD dwCount,
CONST HANDLE* phObjects,
BOOL bWaitAll,
DWORD dwMilliseconds);dwCount:等待内核对象的数量
- phObjects:指针,指向内核对象句柄数组
- bWaitAll: TRUE,等待所有的内核对象都触发时才返回;FALSE,只要有一个内核对象触发就返回
- dwMilliseconds:表示线程愿意花费多少时间来等待对象被触发(时间单位:毫秒),INFINITE表示无限等待
- WaitForMultipleObject的返回值:WAIT_TIMEOUT、WAIT_FAILED与WaitForSingleObject相同;如果设置的是任意一个内核对象触发就返回的话,则返回值是[WAIT_object_0,WAIT_object_0+dwCount-1],得到的数值是内核句柄数组的一个索引;
事件内核对象
事件内核对象包括一个使用计数,一个是否是自动重置事件还是手动重置事件的布尔值,以及另一个表示事件有没有被触发的布尔值。
手动重置事件被触发的时候,正在等待该事件的所有线程都讲变成可调度状态。
自动重置事件被触发的时候,只要一个正在等待该事件的线程会变成可调度状态。
事件内核对象的创建
1 | HANDLE CreateEvent( |
- bManualReset:表示是否创建手动重置对象
- bInitialState:事件初始化为触发状态(TRUE)还是未触发状态(FALSE)
1 | BOOL SetEvent(HANDLE hEvent);//设置事件触发状态 |
信号量内核对象
信号量内核对象用来对资源进行计数。包含一个使用计数,一个最大资源计数和一个目前资源使用计数。
信号量的规则:
- 如果当前资源计数大于0,那么信号量处于触发状态
- 如果当前资源计数等于0,那么信号量处于未触发状态
- 系统绝对不会让当前资源计数变为负数
- 当前资源计数绝对不会大于最大资源计数
信号量内核对象的创建
1 | HANDLE CreateSemaphore( |
- lMaximumCount: 资源的最大使用数量
- lInitialCount: 初始化时资源中有多少可以使用
每当线程成功等待信号量内核对象,则会将当前使用计数减1,最后线程还要调用ReleaseSemaphore来递增信号量的当前资源使用计数。
1 | BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,PLONG plRreviousCount); |
互斥量内核对象
互斥量内核对象用来确保一个一个线程独占对一个资源的访问。互斥量对象包含一个使用计数,线程ID,递归计数。
其中,线程ID用了标识当前占用互斥量的是系统中的哪一个线程,递归计数标识这个线程占用该互斥量的次数。
互斥量对象的规则:
- 如果线程ID为0,那么该互斥对象不为任何线程所占用,它处于触发状态
- 如果线程ID非零值,那么有一个线程已经占用了该互斥量,它处于未触发状态
互斥量对象的创建
1 | HANDLE CreateMutex( |
- bInitialOwner: 互斥量对象的初始状态。FALSE,互斥量对象的线程ID为0,递归计数为0,处于触发状态。
互斥内核对象的一个特点:系统会检查当前线程的ID与互斥量内部记录的线程ID是否相同。如果ID一致,那么系统会让线程保持可调度状态,即使互斥量尚未触发。
1 | BOOL ReleaseMutex(HANDLE hMutex);//释放互斥量,互斥量内部的线程ID置0,递归计数置0,互斥量变成触发状态 |